﻿using System;
using System.IO;
using System.Xml;
namespace Moonlite.ExtensionApi
{
    public class XmlSettingsService : ISettingsService
    {
        private string path;
        private SettingsItemCollection items = new SettingsItemCollection();
        public SettingsItemCollection Items
        {
            get { return items; }
        }
        public string Path
        {
            get { return path; }
            set { path = value; }
        }
        private void WriteItem(ISettingsItem item, XmlNode parentNode, XmlDocument document)
        {
            XmlNode itemNode = null;
            if (item is SettingsCategory)
            {
                SettingsCategory category = (SettingsCategory)item;
                XmlNode categoryNode = document.CreateNode(XmlNodeType.Element, "Category", null);
                XmlAttribute nameAttribute = document.CreateAttribute("Name");
                XmlAttribute guidAttribute = document.CreateAttribute("Guid");
                nameAttribute.Value = category.Name;
                guidAttribute.Value = Guid.NewGuid().ToString();
                categoryNode.Attributes.Append(nameAttribute);
                categoryNode.Attributes.Append(guidAttribute);
                itemNode = categoryNode;
            }
            else if (item is SettingsProperty)
            {
                SettingsProperty property = (SettingsProperty)item;
                if (!(property.Value is string))
                {
                    throw new NotSupportedException("All properties' values must be of type " + typeof(string).Name + ".");
                }
                XmlNode propertyNode = document.CreateNode(XmlNodeType.Element, "PropertyValue", null);
                XmlAttribute nameAttribute = document.CreateAttribute("Name");
                XmlAttribute guidAttribute = document.CreateAttribute("Guid");
                nameAttribute.Value = property.Name;
                guidAttribute.Value = Guid.NewGuid().ToString();
                XmlText value = document.CreateTextNode(property.Value.ToString());
                propertyNode.Attributes.Append(nameAttribute);
                propertyNode.Attributes.Append(guidAttribute);
                propertyNode.AppendChild(value);
                itemNode = propertyNode;
            }
            else
            {
                throw new NotSupportedException("Item type is not supported by the XmlSettingsService. Use the BinarySettingsService for custom item types. Given item type: " + item.GetType().Name);
            }
            if (itemNode != null)
            {
                parentNode.AppendChild(itemNode);
                foreach (ISettingsItem child in item.Children)
                {
                    WriteItem(child, itemNode, document);
                }
            }
        }
        private SettingsItemCollection ReadItem(XmlNode itemNode)
        {
            SettingsItemCollection items = new SettingsItemCollection();
            ISettingsItem item = null;
            if (itemNode.Name.ToLower() == "category")
            {
                SettingsCategory category = new SettingsCategory();
                foreach (XmlAttribute attribute in itemNode.Attributes)
                {
                    switch (attribute.Name.ToLower())
                    {
                        case "name":
                            category.Name = attribute.Value;
                            break;
                        default:
                            break;
                    }
                }
            }
            else if (itemNode.Name.ToLower() == "propertyvalue")
            {
                SettingsProperty property = new SettingsProperty();
                foreach (XmlAttribute attribute in itemNode.Attributes)
                {
                    switch (attribute.Name.ToLower())
                    {
                        case "name":
                            property.Name = attribute.Value;
                            break;
                        default:
                            break;
                    }
                }
                foreach (XmlNode textNode in itemNode.ChildNodes)
                {
                    if (textNode is XmlText)
                    {
                        XmlText actualTextNode = (XmlText)textNode;
                        property.Value = actualTextNode.Value;
                    }
                }
            }
            if (item != null)
            {
                foreach (XmlNode childNode in itemNode.ChildNodes)
                {
                    items.AddRange(ReadItem(childNode));
                }
            }
            return items;
        }
        public void Write()
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path");
            }
            if (File.Exists(path))
            {
                throw new IOException("File at given path already exists.");
            }
            XmlDocument document = new XmlDocument();
            XmlNode rootNode = document.CreateNode(XmlNodeType.Element, "UserSettings", null);
            foreach (ISettingsItem item in items)
            {
                WriteItem(item, rootNode, document);
            }
            document.AppendChild(rootNode);
        }
        public void Read()
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path");
            }
            if (!File.Exists(path))
            {
                throw new IOException("File at given path doesn't exist.");
            }
            XmlDocument document = new XmlDocument();
            document.Load(path);
            foreach (XmlNode rootNode in document.ChildNodes)
            {
                if (rootNode.Name.ToLower() == "usersettings")
                {
                    foreach (XmlNode childNode in rootNode.ChildNodes)
                    {
                        Items.AddRange(ReadItem(childNode));
                    }
                }
            }
        }
        public XmlSettingsService() { }
        public XmlSettingsService(string path)
        {
            this.path = path;
        }
    }
}
